#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>

typedef signed char        int8_t;
typedef short              int16_t;
typedef int                int32_t;
typedef long long          int64_t;
typedef unsigned char      uint8_t;
typedef unsigned short     uint16_t;
typedef unsigned int       uint32_t;
typedef unsigned long long uint64_t;

//*****************************************************************************
#define BYTE_TO_UINT(a, b, c, d)                                               \
	(uint32_t)((d << 24) | (c << 16) | (b << 8) | a)
#define BYTE_TO_USHORT(a, b) (uint16_t)((b << 8) | a)

#define HTONL(A)                                                               \
	(uint32_t)(                                                                  \
	        (((uint32_t)(A)&0xff000000) >> 24) | (((uint32_t)(A)&0x00ff0000) >> 8) | \
	        (((uint32_t)(A)&0x0000ff00) << 8) | (((uint32_t)(A)&0x000000ff) << 24))
#define HTONS(A)                                                               \
	(uint16_t)((((uint16_t)(A)&0xff00) >> 8) | (((uint16_t)(A)&0x00ff) << 8))

#define DATETIME_LEN 6
#define IMEI_STR_LEN 15
#define SIM_ICCID_LEN 20
#define VEHICLE_VIN_LEN 17
#define PFILE_NUM_LEN 16
#define DEVICE_VER_LEN 24
#define TPHONE_NUM_LEN 13

#define MSG_MAX_LEN 1024
#define BUF_SIZE 1024

#define MSG_ID_BIT (uint8_t)0x7e
#define MSG_SOH (uint8_t)0x01
#define MSG_EOT (uint8_t)0x04
#define MSG_DLE (uint8_t)0x10 //固定字节，标示为正常数据传输字
#define MSG_DCL (uint8_t)0x11 //固定字节，升级数据包
#define MSG_TDEV_ID (uint32_t)0x00000001 //终端四字节 ID 编码
#define MSG_SDEV_ID (uint32_t)0x00000000 //服务器四字节 ID 编码
#define MSG_COMPRESS_GZIP_ID (uint8_t)0x08
#define MSG_COMPRESS_UNUSE_ID (uint8_t)0x00
#define MSG_VTYPE_UNUSE (uint8_t)0x00 //重汽的车型码
#define MSG_VTYPE_A7 (uint8_t)0x01
#define MSG_VTYPE_T7 (uint8_t)0x02

#define CMD918 (uint16_t)0xF918 //F918命令字
#define CMD919 (uint16_t)0xF919 //命令字F919
#define CMD929 (uint16_t)0xF929 //F929命令字

#define DATALEN (uint16_t)0x0038 //数据长度
#define DATALEN2 (uint16_t)0x0070 //0038 + 0032数据长度
#define DATALEN3 (uint16_t)0x0100 //0038 + 0032 + 0040 = 0100数据长度

#define BOSCH_Old (uint8_t)0x01
#define BOSCH_New (uint8_t)0x02
#define GAS_ECU_Safe (uint8_t)0x03
#define GAS_ECU_Parameter_Safe (uint8_t)0x04
#define GUOLIU_619_620_DataFlash (uint8_t)0x05

#define Client_Seed (uint32_t)0x1A2B3C4D //四字节的seed
#define Server_Key (uint32_t)0x7FAB77D0 //四字节的key

#define voice_no_check (uint8_t)0x00 //不进行语音和按键提醒，当车辆条件符合直接进行刷写
#define voice_check (uint8_t)0x01 //进行语音提示，刷写需要手动按键确认
#define bin_extend 0 //预留拓展用0x00填充

#define GUOWU_PFile_DataBlock_CB                                               \
	(uint32_t)0x00014000 //国五P文件刷写下发数据块的CB
#define GUOWU_PFile_DataBlock_Code                                             \
	(uint32_t)0x00014000 //国五P文件刷写下发数据块的Code
#define GUOWU_PFile_DataBlock_Data                                             \
	(uint32_t)0x00060000 //国五P文件刷写下发数据块的Data

#define GUOLIU_616P_DataBlock_CB                                               \
	(uint32_t)0x00030000 //国六616P文件刷写下发数据块的CB
#define GUOLIU_616P_DataBlock_Asw0                                             \
	(uint32_t)0x00180000 //国六616P文件刷写下发数据块的Asw0
#define GUOLIU_616P_DataBlock_Asw1                                             \
	(uint32_t)0x00200000 //国六616P文件刷写下发数据块的Asw1
#define GUOLIU_616P_DataBlock_Data                                             \
	(uint32_t)0x00140000 //国六616P文件刷写下发数据块的Data

#define GUOLIU_620P_DataBlock_CB                                               \
	(uint32_t)0x0001E300 //国六620P文件刷写下发数据块的CB
#define GUOLIU_620P_DataBlock_Asw0                                             \
	(uint32_t)0x00125D00 //国六620P文件刷写下发数据块的Asw0
#define GUOLIU_620P_DataBlock_Asw1                                             \
	(uint32_t)0x000D4B00 //国六620P文件刷写下发数据块的Asw1
#define GUOLIU_620P_DataBlock_Asw2                                             \
	(uint32_t)0x000A4800 //国六620P文件刷写下发数据块的Asw2
#define GUOLIU_620P_DataBlock_DS0                                              \
	(uint32_t)0x000C0000 //国六620P文件刷写下发数据块的DS0
#define GUOLIU_620P_DataBlock_DS1                                              \
	(uint32_t)0x00140000 //国六620P文件刷写下发数据块的DS1

#define StartDownload (uint8_t)0x00     //开始下载
#define CompeleteDownload (uint8_t)0x01 //完成下载
#define FailDownload (uint8_t)0xF1      //下载失败
#define StartReFlash (uint8_t)0x02      //开始刷写
#define CompeleteReFlash (uint8_t)0x03  //完成刷写
#define FailReFlash (uint8_t)0xF2       //刷写失败
#define DontSupport (uint8_t)0xF3       //不支持
#define ErrorDescription (uint8_t)0xF4  //错误描述

//*****************************************************************************
#pragma pack(1)
/****************************************************************ZQ协议消息头*/
// T<==>S 重汽协议数据包头(12字节)
typedef struct {
	uint8_t start;       //起始符 固定: 0x7e
	uint8_t soh;         // SOH 固定: 0x01
	uint8_t dle;         // DLE 固定: 0x10 标示为正常数据传输字
	uint16_t pkt_serial; //数据包序号
	char dev;            //‘S’表明 Server，‘T’表明 Termina
	uint32_t dev_id;     // S: 0x00000000 T: ID唯一 自定义
	uint8_t compress_id; //默认为 0x00 不压缩，08：Gzip 压缩
	uint8_t v_type; //车型码: 0x01(A7), 0x02(T7), 若不用, 固定为 0x00
} MSG_HEAD_T;

/*ZQ协议消息尾*/
// T<==>S 重汽协议数据包尾(3字节)
typedef struct {
	uint8_t xr; //除 SOH 字节外，前面所有字节的异或校验值
	uint8_t eot;  // EOT 默认为 0x04
	uint8_t end;  //结束符 固定: 0x7e
} MSG_TAIL_T;

/*ZQ协议消息体*/
// T<==>S 重汽协议数据包 命令字&长度(4字节)
//命令字 + 数据长度
typedef struct {
	uint16_t cmd;
	uint16_t datalen;
} MSG_PDU_ATTR_T;

//终端报文内容实例
typedef struct {
	uint8_t type;
	uint32_t seed;
} MSG_CLIENT_CONETENT_T;

//终端消息
typedef struct {
	MSG_PDU_ATTR_T attr;
	MSG_CLIENT_CONETENT_T clientContent;
} MSG_F918_T;

//平台key
typedef struct {
	uint32_t key;
} MSG_KEY_T;

//平台消息
typedef struct {
	MSG_PDU_ATTR_T attr;
	MSG_KEY_T key;
} MSG_NORMAL_F919_T; //该F919为普通版本，没有涉及加密

// F929命令字格式,添加了AES校验信息
typedef struct {
	uint8_t datatime[32];    //平台时间
	char ftpUserName[15]; // ftp用户名
	char ftpPassword[27]; // ftp密码
	char ftpIp[29];       // ip地址
	char ftpPort[7];      // ftp端口
	char binFile[37];     // bin后缀
} MSG_AES_F929_T;

//添加了AES之后的平台F929消息
typedef struct {
	MSG_PDU_ATTR_T attr; //命令字 + 数据长度
	MSG_AES_F929_T aesF929; //增加了AES校验之后的F929命令
	MSG_KEY_T key; //平台的key
} MSG_F929_T;

//升級bin文件
typedef struct {
	uint32_t fileLength;		//文件总长度
	uint32_t checkSum;			//校验和
	uint8_t constMark[5];		//固定标记，有OTA1、OTA2、OTA3、OTA4、OTA5、OTA6、OTA7、OTA8、OTA9等等，具体看协议
	uint8_t voice_reminder;	//是否声音提醒
	uint8_t extend[19];			//预留拓展用0x00填充
	uint32_t checkSum2;			//校验和2 压缩数据体的校验和
	uint32_t zipFileLength;		//压缩数据体的长度
	uint8_t zipFileData[512];	//压缩数据体
} MSG_UPDATE_BIN_T;

//升级bin之后的F929命令
typedef struct {
	MSG_PDU_ATTR_T attr;        //命令字+数据长度
	MSG_UPDATE_BIN_T updateBin; //升级Bin
	MSG_F929_T f929;            // f929数据报内容
} MSG_UPDATE_F929_T;

/******************************************************************************
 * 负 责 人 : macheng
 * 创建日期 : 2021-03-29
 * 函数功能 : 实现累加校验和
 * 输入参数 :
 * 输出参数 :
 * 返 回 值 :
 * 调用关系 :
 * 其     它 : 开发环境 VS2019
 ******************************************************************************/
unsigned short checksum(unsigned short* pdate, int size) {
	unsigned int sum = 0;
	while (size > 1) {
		sum += *pdate;
		pdate++;
		size -= sizeof(short);
	}
	if (size)
		sum += *(unsigned char*)pdate;
	while (sum >> 16) {
		sum = (sum & 0xffff) + (sum >> 16);
	}
	return ~sum;
}

/******************************************************************************
 * 负 责 人 : macheng
 * 创建日期 : 2021-03-29
 * 函数功能 : 获取时间7字节BCD
 * 输入参数 :
 * 输出参数 :
 * 返 回 值 :
 * 调用关系 :
 * 其     它 : 开发环境 VS2019
 ******************************************************************************/
void time_bcd(uint8_t *bcdTime) {
	time_t rawtime;
	time(&rawtime);

	struct tm* timeinfo;
	timeinfo = localtime(&rawtime);
	sprintf(bcdTime, "%04X%02X%02X%02X%02X%02X",
	        1900 + timeinfo->tm_year,
	        1 + timeinfo->tm_mon,
	        timeinfo->tm_mday,
	        timeinfo->tm_hour,
	        timeinfo->tm_min,
	        timeinfo->tm_sec);
}

/******************************************************************************
 * 负 责 人 : macheng
 * 创建日期 : 2021-03-29
 * 函数功能 : 获取时间7字节BCD
 * 输入参数 :
 * 输出参数 :
 * 返 回 值 :
 * 调用关系 :
 * 其     它 : 开发环境 VS2019
 ******************************************************************************/
void server_updatebin_f929_msg_build(MSG_UPDATE_F929_T *updateF929) {
	memset(updateF929, 0, sizeof(MSG_UPDATE_F929_T));
	//验证s19校验和,并提取固定标识
	char data[] = "S1137AF00A0A0D0000000000000000000000000061\0";
	uint32_t checksum_code = 0;
	checksum_code = checksum((uint16_t*)data, sizeof(data)); //求出包含固定标识在内的校验和
	uint8_t constmark[5];
	uint32_t constmark2 = (uint8_t)0x61;
	memcpy(constmark, data + 4, 4);
	constmark[4] = '\0';
	printf("固定标识 = %s\n", constmark);
	printf("从固定标识开始的校验和1: 0x%04x\n", checksum_code);
	printf("校验和2 = 0x%08x\n", constmark2);
	updateF929->attr.cmd = CMD929;
	updateF929->attr.datalen = DATALEN2;
	updateF929->updateBin.fileLength = DATALEN3; //文件总长度
	updateF929->updateBin.checkSum = checksum_code; //文件校验和
	memcpy(updateF929->updateBin.constMark, constmark, sizeof(constmark)); //固定标识
	updateF929->updateBin.voice_reminder = voice_no_check; //是否声音提醒
	memcpy(updateF929->updateBin.extend, bin_extend, sizeof(updateF929->updateBin.extend));
	memcpy(updateF929->updateBin.checkSum2, constmark2, sizeof(updateF929->updateBin.checkSum2));
	updateF929->updateBin.zipFileLength = sizeof(constmark2);
	memcpy(updateF929->updateBin.zipFileData, data, sizeof(data));
	time_bcd(updateF929->f929.aesF929.datatime);
	memcpy(updateF929->f929.aesF929.ftpUserName, "63766E6176693B\0", sizeof(updateF929->f929.aesF929.ftpUserName));
	memcpy(updateF929->f929.aesF929.ftpPassword, "73686968616E6740323031393B\0", sizeof(updateF929->f929.aesF929.ftpPassword));
	memcpy(updateF929->f929.aesF929.ftpIp, "3131372E37342E3133362E33343B\0", sizeof(updateF929->f929.aesF929.ftpIp));
	memcpy(updateF929->f929.aesF929.ftpPort, "32313B\0", sizeof(updateF929->f929.aesF929.ftpPort));
	memcpy(updateF929->f929.aesF929.binFile, "3037323946344236344D4D48455430365639\0", sizeof(updateF929->f929.aesF929.binFile));
	updateF929->f929.key.key = Server_Key;
}

int main() {
	MSG_UPDATE_F929_T updateF929;
	server_updatebin_f929_msg_build(&updateF929);
	printf("%x\n", updateF929.updateBin.checkSum);
	return 0;
}
